package org.opengauss.evaluate.visitor;

import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.expr.*;
import com.github.javaparser.ast.nodeTypes.NodeWithName;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import com.github.javaparser.resolution.MethodUsage;
import com.github.javaparser.resolution.declarations.ResolvedFieldDeclaration;
import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
import com.github.javaparser.resolution.types.ResolvedReferenceType;
import com.github.javaparser.resolution.types.ResolvedType;
import org.apache.commons.lang.StringUtils;
import org.opengauss.evaluate.utils.SqlReplaceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author biao.yang
 * @Description:
 * @date 2025/3/25 15:05
 */
public class JavaAnnotationVisitor extends VoidVisitorAdapter<Void> {

    private static final Logger LOGGER = LoggerFactory.getLogger(JavaAnnotationVisitor.class);
    private static final String SQL_REGEX = "Select|Insert|Update|Delete";

    private List<String> parserSql = new ArrayList<>();

    public List<String> getParserSql() {
        return parserSql;
    }

    public void setParserSql(List<String> parserSql) {
        this.parserSql = parserSql;
    }

    @Override
    public void visit(MethodDeclaration method, Void arg) {
        super.visit(method, arg);

        boolean hasSqlAnnotation = method.getAnnotations().stream()
                .map(NodeWithName::getNameAsString)
                .anyMatch(annotationName -> annotationName.matches(SQL_REGEX));
        if(!hasSqlAnnotation){
            return;
        }

        // 收集方法参数的名称和类型（处理@Param注解）
        Map<String, ResolvedType> parameterTypes = new HashMap<>();
        if(!method.getParameters().isEmpty()){
            convertParameterTypes(method, parameterTypes);
        }

        // 处理注解中的SQL
        method.getAnnotations().forEach(annotation -> {
            String annotationName = annotation.getNameAsString();
            if (annotationName.matches(SQL_REGEX)) {
                List<String> sqlParts = new ArrayList<>();
                if (annotation instanceof SingleMemberAnnotationExpr) {
                    extractSqlValue(((SingleMemberAnnotationExpr) annotation).getMemberValue(), sqlParts);
                } else if (annotation instanceof NormalAnnotationExpr) {
                    ((NormalAnnotationExpr) annotation).getPairs().forEach(pair -> {
                        if ("value".equals(pair.getNameAsString())) {
                            extractSqlValue(pair.getValue(), sqlParts);
                        }
                    });
                }
                if (!sqlParts.isEmpty()) {
                    String sql = String.join(" ", sqlParts);
                    LOGGER.info("Origin SQL: {}", sql);

                    final String dynamicSql = SqlReplaceUtils.analyzeDynamicParams(sql, parameterTypes);
                    parserSql.add(dynamicSql);
                }
            }
        });
    }


    private static void convertParameterTypes(MethodDeclaration method, Map<String, ResolvedType> parameterTypes){
        final int paramSize = method.getParameters().size();
        for (Parameter parameter : method.getParameters()) {
            String paramName = parameter.getNameAsString();
            if(paramSize > 1){
                // 检查是否有@Param注解
                final boolean present = parameter.getAnnotationByName("Param").isPresent();
                if(!present){
                    continue;
                }
                parameter.getAnnotationByName("Param").ifPresent(annotation -> {
                    if (annotation instanceof SingleMemberAnnotationExpr) {
                        Expression valueExpr = ((SingleMemberAnnotationExpr) annotation).getMemberValue();
                        if (valueExpr instanceof StringLiteralExpr) {
                            final String value = ((StringLiteralExpr) valueExpr).getValue();
                            LOGGER.info("{} = {}", paramName, value);
                        }
                    }
                });
            }

            try {
                ResolvedType resolvedType = parameter.getType().resolve();
                // 解析泛型中的实体类
                if (resolvedType.isReferenceType()) {
                    ResolvedReferenceType refType = resolvedType.asReferenceType();
                    ResolvedReferenceTypeDeclaration typeDeclaration = refType.getTypeDeclaration().orElse(null);
                    if(typeDeclaration != null){
                        String rawTypeName = typeDeclaration.getQualifiedName();
                        if (rawTypeName.equals("java.util.List") || rawTypeName.equals("java.util.Collection")) {
                            // 获取泛型参数（如 OperatorReportDO）
                            List<ResolvedType> typeParams = refType.typeParametersValues();
                            if (!typeParams.isEmpty()) {
                                ResolvedType genericType = typeParams.get(0);
                                parameterTypes.put(paramName + "-generic", genericType);
                                LOGGER.info("集合泛型参数类型: {}", genericType.describe());
                            }
                        }else{
                            parameterTypes.put(paramName, refType);
                        }
                    }
                }else{
                    parameterTypes.put(paramName, resolvedType);
                }
            } catch (Exception e) {
                LOGGER.error("param parse exception. ", e);
            }
        }
    }


    private void extractSqlValue(Expression expr, List<String> sqlParts) {
        if (expr instanceof StringLiteralExpr) {
            sqlParts.add(((StringLiteralExpr) expr).getValue());
        } else if (expr instanceof BinaryExpr && ((BinaryExpr) expr).getOperator() == BinaryExpr.Operator.PLUS) {
            extractSqlValue(((BinaryExpr) expr).getLeft(), sqlParts);
            extractSqlValue(((BinaryExpr) expr).getRight(), sqlParts);
        } else if (expr instanceof ArrayInitializerExpr) {
            ((ArrayInitializerExpr) expr).getValues().forEach(value -> extractSqlValue(value, sqlParts));
        }
    }

}
